/******************************************************************************
Module:  LastMsgBoxInfoLib.cpp
Notices: Copyright (c) 2000 Jeffrey Richter
******************************************************************************/

#include <stdafx.h>

#include <CmnHdr.h>
#include <WindowsX.h>
#include <tchar.h>
#include <stdio.h>
#include "APIHook.h"

#include "TLBBHookLib.h"
#include "HookClient.h"

#include <CommonUtil/Logger.h>

#define M_HookWndProc	1

///////////////////////////////////////////////////////////////////////////////

// Since we do DLL injection with Windows' hooks, we need to save the hook
// handle in a shared memory block (Windows 2000 actually doesn't need this)
#pragma data_seg("Shared")

	HHOOK g_hCallWndHook = NULL;
	DWORD g_dwHookThreadId = 0;
	HWND g_hwnd = NULL;

#if M_HookWndProc
	volatile LONG g_OldWndProc = 0;
#endif

#pragma data_seg()
#pragma comment(linker, "/Section:Shared,rws")

// Nonshared variables
HINSTANCE g_hinstDll = NULL;

///////////////////////////////////////////////////////////////////////////////


// Prototypes for the hooked functions
//typedef int (WINAPI *PFNMESSAGEBOXA)(HWND hWnd, PCSTR pszText, PCSTR pszCaption, UINT uType);
//typedef int (WINAPI *PFNMESSAGEBOXW)(HWND hWnd, PCWSTR pszText, PCWSTR pszCaption, UINT uType);
//
//typedef HWND (WINAPI *PFN_GetForegroundWindow)(VOID);
//typedef HWND (WINAPI *PFN_GetActiveWindow)(VOID);
//typedef HWND (WINAPI *PFN_GetCapture)(VOID);

/*
//Wsock32.dll
int PASCAL FAR recv (
IN SOCKET s,
char FAR * buf,
IN int len,
IN int flags);
*/

// We need to reference these variables before we create them.
//extern CAPIHook g_MessageBoxA;
//extern CAPIHook g_MessageBoxW;

//extern CAPIHook g_GetForegroundWindow;
//extern CAPIHook g_GetActiveWindow;
//extern CAPIHook g_GetCapture;

///////////////////////////////////////////////////////////////////////////////

#if 0

// This function sends the MessageBox info to our main dialog box
void SendLastMsgBoxInfo(BOOL fUnicode, PVOID pvCaption, PVOID pvText, int nResult)
{
	// Get the pathname of the process displaying the message box
	char szProcessPathname[MAX_PATH];
	GetModuleFileNameA(NULL, szProcessPathname, MAX_PATH);

	// Convert the return value into a human-readable string
	PCSTR pszResult = "(Unknown)";
	switch (nResult) {
	  case IDOK:       pszResult = "Ok";        break;
	  case IDCANCEL:   pszResult = "Cancel";    break;
	  case IDABORT:    pszResult = "Abort";     break;
	  case IDRETRY:    pszResult = "Retry";     break;
	  case IDIGNORE:   pszResult = "Ignore";    break;
	  case IDYES:      pszResult = "Yes";       break;
	  case IDNO:       pszResult = "No";        break;
	  case IDCLOSE:    pszResult = "Close";     break;
	  case IDHELP:     pszResult = "Help";      break;
	  case IDTRYAGAIN: pszResult = "Try Again"; break;
	  case IDCONTINUE: pszResult = "Continue";  break;
	}

	// Construct the string to send to the main dialog box
	char sz[2048];
	wsprintfA(sz, fUnicode 
		? "Process: (%d) %s\r\nCaption: %S\r\nMessage: %S\r\nResult: %s"
		: "Process: (%d) %s\r\nCaption: %s\r\nMessage: %s\r\nResult: %s",
		GetCurrentProcessId(), szProcessPathname,
		pvCaption, pvText, pszResult);

	// Send the string to the main dialog box
	COPYDATASTRUCT cds = { 0, lstrlenA(sz) + 1, sz };
	FORWARD_WM_COPYDATA(FindWindow(NULL, TEXT("Last MessageBox Info")), 
		NULL, &cds, SendMessage);
}

// This is the MessageBoxW replacement function
int WINAPI Hook_MessageBoxW(HWND hWnd, PCWSTR pszText, LPCWSTR pszCaption, UINT uType)
{

	// Call the original MessageBoxW function
	int nResult = ((PFNMESSAGEBOXW)(PROC) g_MessageBoxW)
		(hWnd, pszText, pszCaption, uType);

	// Send the information to the main dialog box
	SendLastMsgBoxInfo(TRUE, (PVOID) pszCaption, (PVOID) pszText, nResult);

	// Return the result back to the caller
	return(nResult);
}

// This is the MessageBoxA replacement function
int WINAPI Hook_MessageBoxA(HWND hWnd, PCSTR pszText, PCSTR pszCaption, UINT uType)
{

	// Call the original MessageBoxA function
	int nResult = ((PFNMESSAGEBOXA)(PROC) g_MessageBoxA)
		(hWnd, pszText, pszCaption, uType);

	// Send the information to the main dialog box
	SendLastMsgBoxInfo(FALSE, (PVOID) pszCaption, (PVOID) pszText, nResult);

	// Return the result back to the caller
	return(nResult);
}
#endif

///////////////////////////////////////////////////////////////////////////////

#if 0
HWND WINAPI Hook_GetForegroundWindow()
{
	if (g_dwHookThreadId == GetCurrentThreadId()
		&&
		g_hwnd
		)
	{
		return g_hwnd;
	}
	else
	{
		 //Call the original GetForegroundWindow function
		return ((PFN_GetForegroundWindow)(PROC) g_GetForegroundWindow)();
	}
}

HWND WINAPI Hook_GetActiveWindow()
{
	if (g_dwHookThreadId == GetCurrentThreadId()
		&&
		g_hwnd
		)
	{
		return g_hwnd;
	}
	else
	{
		//Call the original GetForegroundWindow function
		return ((PFN_GetActiveWindow)(PROC) g_GetActiveWindow)();
	}
}

HWND WINAPI Hook_GetCapture()
{
	if (g_dwHookThreadId == GetCurrentThreadId()
		&&
		g_hwnd
		)
	{
		return g_hwnd;
	}
	else
	{
		//Call the original GetCapture function
		return ((PFN_GetCapture)(PROC) g_GetCapture)();
	}
}
#endif

///////////////////////////////////////////////////////////////////////////////


// Hook the MessageBoxA and MessageBoxW functions
//CAPIHook g_MessageBoxA("User32.dll", "MessageBoxA", (PROC) Hook_MessageBoxA, TRUE);
//CAPIHook g_MessageBoxW("User32.dll", "MessageBoxW", (PROC) Hook_MessageBoxW, TRUE);
//
//CAPIHook g_GetForegroundWindow("User32.dll", "GetForegroundWindow", (PROC) Hook_GetForegroundWindow, TRUE);
//CAPIHook g_GetActiveWindow("User32.dll", "GetActiveWindow", (PROC) Hook_GetActiveWindow, TRUE);
//CAPIHook g_GetCapture("User32.dll", "GetCapture", (PROC) Hook_GetCapture, TRUE);

///////////////////////////////////////////////////////////////////////////////


//static LRESULT WINAPI GetMsgProc(int code, WPARAM wParam, LPARAM lParam)
//{
//
//	// NOTE: On Windows 2000, the 1st parameter to CallNextHookEx can 
//	// be NULL.  On Windows 98, it must be the hook handle.
//	return(CallNextHookEx(g_hGetMsghook, code, wParam, lParam));
//}


static LRESULT WINAPI CallWndProc(int code, WPARAM wParam, LPARAM lParam)
{
	// NOTE: On Windows 2000, the 1st parameter to CallNextHookEx can 
	// be NULL.  On Windows 98, it must be the hook handle.
	return(CallNextHookEx(g_hCallWndHook, code, wParam, lParam));
}


///////////////////////////////////////////////////////////////////////////////

// Returns the HMODULE that contains the specified memory address
static HMODULE ModuleFromAddress(PVOID pv)
{
	MEMORY_BASIC_INFORMATION mbi;
	return((VirtualQuery(pv, &mbi, sizeof(mbi)) != 0) ? (HMODULE) mbi.AllocationBase : NULL);
}


///////////////////////////////////////////////////////////////////////////////


BOOL WINAPI InstallHook(BOOL fInstall, DWORD dwThreadId, HWND hwnd)
{

	BOOL fOk = FALSE;

	if (fInstall)
	{
		//chASSERT(g_hGetMsghook == NULL); // Illegal to install twice in a row

		g_dwHookThreadId	= dwThreadId;
		g_hwnd				= hwnd;

		// Install the Windows' hook
		//g_hGetMsghook = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, ModuleFromAddress(LastMsgBoxInfo_HookAllApps), dwThreadId);
		//fOk = (g_hGetMsghook != NULL);

		//LONG l = ::GetWindowLong(g_hwnd, GWL_EXSTYLE);
		//::SetWindowLong(g_hwnd, GWL_EXSTYLE, l | WS_EX_LAYERED);
		//::SetLayeredWindowAttributes(g_hwnd, 0, 255, LWA_ALPHA);

		g_hCallWndHook = SetWindowsHookEx(WH_CALLWNDPROC, CallWndProc, ModuleFromAddress(InstallHook), dwThreadId);
		fOk = (g_hCallWndHook != NULL);
	}
	else
	{
		//chASSERT(g_hGetMsghook != NULL); // Can't uninstall if not installed

		//fOk = UnhookWindowsHookEx(g_hGetMsghook);
		//g_hGetMsghook = NULL;

#if M_HookWndProc
		if (g_hwnd && g_OldWndProc)
		{
			Log_Debug("UnHook WndProc...");

			::SetWindowLong(g_hwnd, GWL_WNDPROC, (LONG)g_OldWndProc);
			g_OldWndProc = 0;
		}
#endif

		if (g_hCallWndHook)
		{
			fOk = UnhookWindowsHookEx(g_hCallWndHook);
			g_hCallWndHook = 0;
		}
	}

	return(fOk);
}

#if M_HookWndProc
LRESULT WINAPI HookMainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	if( hWnd == g_hwnd ) 
	{ 
		if( message == WM_MOVE)
		{ 
			int xPos = (int)(short) LOWORD(lParam);   // horizontal position 
			int yPos = (int)(short) HIWORD(lParam);   // vertical position 
			if (xPos < 0 || yPos < 0)
			{
				Log_Debug("Set LayeredWindow, xPos: " << xPos <<", yPos: " << yPos);

				//LONG l = ::GetWindowLong(g_hwnd, GWL_EXSTYLE);
				//::SetWindowLong(g_hwnd, GWL_EXSTYLE, l | WS_EX_LAYERED);
				//::SetLayeredWindowAttributes(g_hwnd, 0, 255, LWA_ALPHA);
			}
			else
			{
				Log_Debug("Clear LayeredWindow, xPos: " << xPos <<", yPos: " << yPos);

				//LONG l = ::GetWindowLong(g_hwnd, GWL_EXSTYLE);
				//::SetWindowLong(g_hwnd, GWL_EXSTYLE, l & ~WS_EX_LAYERED);
			}
		}
	} 

	if (g_OldWndProc != 0)
		return ((WNDPROC)g_OldWndProc)(hWnd, message, wParam, lParam);
	else
		return 0;

}
#endif


BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD fdwReason, PVOID fImpLoad) {

	switch (fdwReason)
	{

	case DLL_PROCESS_ATTACH:
		// DLL is attaching to the address space of the current process.
		g_hinstDll = hinstDll;

		if (g_dwHookThreadId == ::GetCurrentThreadId())
		{
			printf("=====This is a client.\r\n");

			Mem_New HookClient(g_hwnd);
			Log_Debug("HooClient installed, thread id: " << ::GetCurrentThreadId());

#if M_HookWndProc
			Log_Debug("Hook WndProc...");

			g_OldWndProc = ::GetWindowLong(g_hwnd, GWL_WNDPROC);
			::SetWindowLong(g_hwnd, GWL_WNDPROC, (LONG)HookMainWndProc);
#endif
		}
		else
		{
			printf("=====This is a server.\r\n");
		}

		break;

	case DLL_THREAD_ATTACH:
		// A new thread is being created in the current process.
		break;

	case DLL_THREAD_DETACH:
		// A thread is exiting cleanly.
		break;

	case DLL_PROCESS_DETACH:
		// The calling process is detaching the DLL from its address space.

		if (g_hwnd)
		{
			Log_Debug("HooClient uninstalling, thread id: " << ::GetCurrentThreadId());

			Log_Debug("Clear LayeredWindow...");

			//LONG l = ::GetWindowLong(g_hwnd, GWL_EXSTYLE);
			//::SetWindowLong(g_hwnd, GWL_EXSTYLE, l & ~WS_EX_LAYERED);

			Mem_Delete HookClient::getSingletonPtr();
			g_hwnd = 0;
		}

		break;
	}
	return(TRUE);
}
